//
//  TFURLSessionManagerTaskDelegate.m
//  AFN_Test
//
//  Created by Guo Rui on 2017/9/18.
//  Copyright © 2017年 GR Harry. All rights reserved.
//

#import "TFURLSessionManagerTaskDelegate.h"
#import "TFURLSessionManager.h"


NSString * const TFNetworkingTaskDidResumeNotifcation = @"com.timeface.networking.task.resume";
NSString * const TFNetworkingTaskDidSuspendNotifcation = @"com.timeface.networking.task.suspend";
NSString * const TFNetworkingTaskDidCompletionNotification = @"com.timeface.networking.task.completion";

NSString * const TFNetworkingTaskDidCompleteResponseSerializerKey = @"com.timeface.networking.task.complete.responseserializer";
NSString * const TFNetworkingTaskDidCompleteSerializedResponseKey = @"com.timeface.networking.task.complete.serializedresponse";
NSString * const TFNetworkingTaskDidCompleteResponseDataKey = @"com.timeface.networking.task.complete.responsedata";
NSString * const TFNetworkingTaskDidCompleteAssetPathKey = @"com.timeface.networking.task.complete.assetpath";
NSString * const TFNetworkingTaskDidCompleteErrorKey = @"com.timeface.networking.task.complete.error";

NSString * const TFURLSessionTaskDidFailToMoveFileNotifcation = @"com.timeface.networking.session.download.failToMoveFile";

@implementation TFURLSessionManagerTaskDelegate

static dispatch_group_t url_session_manager_completion_group() {
    static dispatch_group_t url_session_manager_completion_group = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        url_session_manager_completion_group = dispatch_group_create();
    });
    
    return url_session_manager_completion_group;
}

static dispatch_queue_t url_session_manager_processing_queue() {
    static dispatch_queue_t url_session_manager_processing_queue = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        url_session_manager_processing_queue = dispatch_queue_create("com.timeface.networking.session.manager.process", DISPATCH_QUEUE_CONCURRENT);
    });
    
    return url_session_manager_processing_queue;
}

- (instancetype)initWithTask:(NSURLSessionTask *)task
{
    if (nil == [super init]) {
        return nil;
    }
    
    _mutableData = [[NSMutableData alloc] init];
    _uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
    _downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
    
    __weak typeof(task) weakTask = task;
    for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ]) {
        progress.totalUnitCount = NSURLSessionTransferSizeUnknown;
        
        progress.cancellable = YES;
        progress.cancellationHandler = ^{
            [weakTask cancel];
        };
        
        progress.pausable = YES;
        progress.pausingHandler = ^{
            [weakTask suspend];
        };
        
        if ([progress respondsToSelector:@selector(setResumingHandler:)]) {
            progress.resumingHandler = ^{
                [weakTask resume];
            };
        }
        
        [progress addObserver:self
                   forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                      options:NSKeyValueObservingOptionNew
                      context:NULL];
    }
    
    
    return self;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    if ([object isEqual:self.uploadProgress]) {
        if (self.uploadProgressBlock) {
            self.uploadProgressBlock(object);
        }
    }
    else if ([object isEqual:self.downloadProgress]) {
        if (self.downloadProgressBlock) {
            self.downloadProgressBlock(object);
        }
    }
}

- (void)dealloc {
    [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
    [self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
    NSLog(@"\n************\n %@ dealloc \n************", self.class);
}

#pragma mark - NSURLSessionTaskDelegate
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    __strong TFURLSessionManager *manager = self.manager;
    __block id responseObject = nil;
    __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithCapacity:0];
    
    userInfo[TFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
    
    NSData *data = nil;
    if (self.mutableData) {
        data = [self.mutableData copy];
        self.mutableData = nil;
    }
    
    if (self.downloadFileURL) {
        userInfo[TFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
    }
    else if (data) {
        userInfo[TFNetworkingTaskDidCompleteResponseDataKey] = data;
    }
    
    if (error) {
        userInfo[TFNetworkingTaskDidCompleteErrorKey] = error;
        
        dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
            if (self.completionHandler) {
                self.completionHandler(task.response, responseObject, error);
            }
            
            dispatch_async(dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:TFNetworkingTaskDidCompletionNotification object:task userInfo:userInfo];
            });
        });
    }
    else {
        dispatch_async(url_session_manager_processing_queue(), ^{
            NSError *seralizationError = nil;
            responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&seralizationError];
            
            if (self.downloadFileURL) {
                responseObject = self.downloadFileURL;
            }
            
            if (responseObject) {
                userInfo[TFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
            }
            
            
            if (seralizationError) {
                userInfo[TFNetworkingTaskDidCompleteErrorKey] = seralizationError;
            }
            
            dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
                if (self.completionHandler) {
                    self.completionHandler(task.response, responseObject,seralizationError);
                }
                
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:TFNetworkingTaskDidCompletionNotification object:task userInfo:userInfo];
                });
            });
        });
    }
}

#pragma mark - NSURLSessionDataTaskDelegate
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
    self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive;
    self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived;
    
    [self.mutableData appendData:data];
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
    self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
    self.uploadProgress.completedUnitCount = task.countOfBytesSent;
}

#pragma mark - NSURLSessionDownloadTaskDelegate
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite;
    self.downloadProgress.completedUnitCount = totalBytesWritten;
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
    self.downloadProgress.totalUnitCount = expectedTotalBytes;
    self.downloadProgress.completedUnitCount = fileOffset;
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
    self.downloadFileURL = nil;
    
    if (self.downloadTaskDidFinishDownloading) {
        self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
        
        NSError *fileManageError = nil;
        
        if (self.downloadFileURL) {
            if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManageError]) {
                [[NSNotificationCenter defaultCenter] postNotificationName:TFURLSessionTaskDidFailToMoveFileNotifcation object:downloadTask userInfo:fileManageError.userInfo];
            }
        }
    }
}

@end
